from typing import List

import numpy as np
from shapely.geometry import Point, Polygon
import pyproj

from model.es.card import FencePoint
from model.wristband_models import LocationData


def is_point_in_geo_fence(x: float, y: float, fence: List[FencePoint]) -> bool:
    """
    判断地理点是否在字典格式围栏内
    :param x: 经度 key
    :param y: 纬度 key
    :param fence: 围栏字典列表
    :return: 布尔值检测结果
    """
    # 数据格式转换
    polygon = [(p.longitude, p.latitude) for p in fence]
    n = len(polygon)
    inside = False
    eps = 1e-9

    for i in range(n):
        p1 = polygon[i]
        p2 = polygon[(i + 1) % n]
        x1, y1 = p1
        x2, y2 = p2

        # 检查点是否在边上
        if (min(x1, x2) - eps <= x <= max(x1, x2) + eps and
                min(y1, y2) - eps <= y <= max(y1, y2) + eps):
            cross = (x2 - x1) * (y - y1) - (y2 - y1) * (x - x1)
            if abs(cross) < eps:
                return True

                # 射线法交点计算
        if (y1 > y) != (y2 > y):
            x_intersect = (y - y1) * (x2 - x1) / (y2 - y1) + x1
            if x < x_intersect + eps:
                inside = not inside

    return inside


async def calculate_distance(point: LocationData, polygon:List[FencePoint]):
    # 计算多边形中心点
    center_lon = sum(p.longitude for p in polygon) / len(polygon)
    center_lat = sum(p.latitude for p in polygon) / len(polygon)

    # 确定UTM区域
    utm_zone = int((center_lon + 180) / 6) + 1
    utm_hemisphere = 'north' if center_lat >= 0 else 'south'
    utm_crs = f'EPSG:326{utm_zone}' if utm_hemisphere == 'north' else f'EPSG:327{utm_zone}'

    # 创建投影转换器
    transformer = pyproj.Transformer.from_crs(
        "EPSG:4326",  # WGS84
        utm_crs,
        always_xy=True  # 经度在前，纬度在后
    )

    # 转换坐标（修正顺序）
    point_utm = transformer.transform(point.longitude, point.latitude)
    polygon_utm = [transformer.transform(item.longitude, item.latitude) for item in polygon]

    # 创建Shapely对象
    point_shapely = Point(point_utm)
    polygon_shapely = Polygon(polygon_utm)

    # 计算距离（米）
    return point_shapely.distance(polygon_shapely)  # 传入多边形对象


def format_polygon(polygon_dicts, latitude_key: str = 'latitude',
                   longitude_key: str = 'longitude'):
    """将字典列表转换为坐标元组列表"""
    return [{"longitude": p[longitude_key], "latitude": p[latitude_key]} for p in polygon_dicts]


def process_location_data(data, threshold=10):
    # 将数据转换为结构化NumPy数组
    global imeis
    dtype = [('date_bucket', 'O'), ('imei', 'U15'),
             ('lat', np.float64), ('lon', np.float64)]
    np_data = np.array([(d['date_bucket'], d['imei'],
                         float(d['location']['lat']),
                         float(d['location']['lon']))
                        for d in data], dtype=dtype)

    # 按时间窗口分组
    unique_dates = np.unique(np_data['date_bucket'])
    imei_pairs = {}

    for date in unique_dates:
        window = np_data[np_data['date_bucket'] == date]
        imeis = window['imei']
        imeis = np.array(imeis, dtype=np.str_)
        lats = window['lat']
        lons = window['lon']

        # 仅计算上三角矩阵，避免对称重复
        n = len(lats)
        i_indices, j_indices = np.triu_indices(n, k=1)  # k=1排除自身

        # 提取对应坐标
        lat_i = lats[i_indices]
        lon_i = lons[i_indices]
        lat_j = lats[j_indices]
        lon_j = lons[j_indices]

        # 计算距离（向量化）
        distances = haversine_vectorized(
            (lat_i, lon_i),
            (lat_j, lon_j)
        )

        # 提取满足条件的索引
        valid = np.where(distances <= threshold)[0]

        for idx in valid:
            i = i_indices[idx]
            j = j_indices[idx]

            # 固定IMEI对顺序
            imei1, imei2 = sorted((imeis[i], imeis[j]))

            # 记录详细信息
            record = {
                'date_bucket': date,
                'distance': distances[idx],
                'locations': {
                    imeis[i]: (lats[i], lons[i]),
                    imeis[j]: (lats[j], lons[j])
                }
            }

            # 存储结果
            pair_key = f"{imei1}_{imei2}"
            if pair_key not in imei_pairs:
                imei_pairs[pair_key] = {'count': 0, 'details': []}
            imei_pairs[pair_key]['count'] += 1
            imei_pairs[pair_key]['details'].append(record)

    # 转换为结构化结果

    return imei_pairs


def process_step_data(data, threshold=100):
    # 将数据转换为结构化NumPy数组
    dtype = [('date_bucket', 'O'), ('imei', 'U15'), ('step_hour', np.int32)]
    np_data = np.array([(d['date_bucket'], d['imei'], d['step_hour'])
                        for d in data], dtype=dtype)

    # 按时间窗口分组
    unique_dates = np.unique(np_data['date_bucket'])
    device_pairs = {}

    for date in unique_dates:
        window = np_data[np_data['date_bucket'] == date]
        devices = window['imei']
        steps = window['step_hour']

        # 创建步数差矩阵（绝对值）
        step_diff_matrix = np.abs(steps[:, np.newaxis] - steps)

        # 仅保留满足条件的配对（排除自身）
        mask = (step_diff_matrix <= threshold) & (step_diff_matrix > 0)
        indices = np.where(mask)

        for i, j in zip(*indices):
            # 固定设备对顺序（按字典序）
            pair = tuple(sorted((devices[i], devices[j])))
            step_i, step_j = steps[i], steps[j]

            # 记录详细信息
            record = {
                'date_bucket': date,
                'step_diff': step_diff_matrix[i, j],
                'device_steps': {
                    devices[i]: step_i,
                    devices[j]: step_j
                }
            }

            if pair not in device_pairs:
                device_pairs[pair] = {'count': 0, 'details': []}
            device_pairs[pair]['count'] += 1
            device_pairs[pair]['details'].append(record)
    # 转换为结构化结果
    return device_pairs


def haversine_vectorized(coords1, coords2):
    """向量化哈弗赛恩距离计算（米）"""
    lat1, lon1 = np.radians(coords1)
    lat2, lon2 = np.radians(coords2)

    dlat = lat2 - lat1
    dlon = lon2 - lon1

    a = np.sin(dlat / 2) ** 2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon / 2) ** 2
    c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1 - a))
    return 6371 * 1000 * c  # 米
